Skip to content

Add code quality issue listing to the CLI - #130

Merged
Ibrahimrahhal merged 7 commits into
mainfrom
cursor/expose-code-quality-findings-c31f
Aug 5, 2026
Merged

Add code quality issue listing to the CLI#130
Ibrahimrahhal merged 7 commits into
mainfrom
cursor/expose-code-quality-findings-c31f

Conversation

@Ibrahimrahhal

@Ibrahimrahhal Ibrahimrahhal commented Jul 20, 2026

Copy link
Copy Markdown
Member

Summary

The CLI only exposed security (SAST) and SCA findings — code quality findings were not reachable at all. This adds first-class support for listing them, against the endpoints documented at docs.corgea.app: GET /api/v1/issues/code-quality (project-scoped) and GET /api/v1/scan/{scan_id}/issues/quality (scan-scoped).

Changes

  • corgea list --code-quality (alias --quality, short -q): lists code quality findings for the current project or a specific --scan-id, in the same table / --json formats as --issues. The Category column shows the code quality label (e.g. Maintainability) in place of a CWE id.
  • utils/api.rs: new get_quality_issues(). The endpoint and query are built by quality_issues_request(), which sends the project through query rather than string interpolation (project names can contain &/?/#), and the HTTP status is read before the body so a bare 404 surfaces as a missing scan rather than a parse failure.
  • Project resolution: the code quality listing goes through ProjectSelector / resolve_project_or_exit(), exactly like --issues and the default scan listing, so --project-name, --repo, and repo-URL auto-resolution all behave identically (COR-1577).
  • Validation: --issues, --sca-issues, and --code-quality are mutually exclusive.
  • Blocking rules: blocking-rules enrichment stays gated to security listings. Previously list --code-quality --scan-id would still call check_blocking_rules (hard-exiting on any failure even after the code quality fetch succeeded) and could render Blocking columns driven by non-code-quality findings.
  • Skill: --code-quality documented in skills/corgea/SKILL.md.

Testing

  • tests/list_code_quality.rs — end-to-end coverage over a recording stub: both documented request targets are asserted, --quality/-q reach the same route, the canonical project resolved from the git remote scopes the request, the project name is percent-encoded, --code-quality --scan-id never dials check_blocking_rules (unstubbed, so reaching it would exit 1) and never dials /projects, a 404 reports the missing scan instead of a parse failure, and every pair of issue-kind flags is rejected.
  • src/utils/api.rs — unit tests pinning the two request paths plus their query params, and the code quality response deserialization (label classification with a null description).
  • ./harness ci — clippy (strict), format check, dep audit, 617 tests and the coverage gate all pass.

Example

corgea list --code-quality
corgea list --quality --scan-id <scan_id>
corgea list --code-quality --project-name my-repo --json
Open in Web Open in Cursor 

cursoragent and others added 2 commits July 20, 2026 09:17
Add 'corgea list --code-quality' (alias --quality) to list code quality
findings, mirroring --issues but hitting the code quality endpoints
(/scan/<id>/issues/quality and /issues/code-quality).

- Add get_quality_issues() to the API client.
- Deserialize the new 'type' discriminator on issues (optional).
- Make --issues, --sca-issues, and --code-quality mutually exclusive.
- Add a deserialization test for code quality issue responses.

Co-authored-by: ibrahim <ibrahim@corgea.com>
Code quality and security issues come from separate endpoints, so the CLI
does not need a 'type' discriminator on the Issue struct. Keeps the CLI in
sync with the API response, which no longer emits the field.

Co-authored-by: ibrahim <ibrahim@corgea.com>
@Ibrahimrahhal
Ibrahimrahhal marked this pull request as ready for review July 20, 2026 09:39
CI runs clippy with -D warnings; adding the --code-quality flag pushed
list::run to 8 arguments (limit 7). Allow the lint here, matching the
existing pattern used in blast.rs and deps/ecosystems/evaluate.rs.

Co-authored-by: ibrahim <ibrahim@corgea.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Production review on c72c3d6 (vs main). Three merge blockers below: CQ listing incorrectly inherits security blocking-rule failure modes, the new HTTP client ignores status codes during a companion-API rollout, and the only new test does not lock the endpoint contract.

Also confirm the doghouse companion (PR body cites #1715) is deployed before shipping this CLI — the feature hard-depends on those endpoints being healthy.

Open in Web View Automation 

Sent by Cursor Automation: pr-flow

Comment thread src/list.rs Outdated
*page_size,
scan_id.clone(),
) {
} else if *issues || *code_quality {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--code-quality inherits security blocking-rule side effects (including hard failure).

Folding CQ into the *issues branch means corgea list --code-quality --scan-id ... still runs check_blocking_rules (lines 162-189) and exits 1 on any failure, even after get_quality_issues already succeeded.

Impact: A blocking-rules API blip (or security-only block: true) makes CQ listing unusable, or adds empty Blocking columns driven by non-CQ findings.

Fix: Gate enrichment on security listing only, e.g. if scan_id.is_some() && !*code_quality { ... }. Or, if CQ rows can legitimately be blocked, soft-fail the enrichment (log + continue) and set render_blocking_rules only when at least one returned CQ issue id is in the blocking map. Add a regression test for --code-quality --scan-id that stubs blocking-rules failure and asserts the CQ table/JSON still prints.

Comment thread src/utils/api.rs Outdated
Comment on lines +546 to +553
let response = match client.get(&url).send() {
Ok(res) => {
check_for_warnings(res.headers(), res.status());
res
}
Err(e) => return Err(format!("Failed to send request: {}", e).into()),
};
let response_text = response.text()?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New client ignores HTTP status; non-JSON errors become opaque parse/auth failures.

get_quality_issues always reads the body and serde_json::from_strs with no status().is_success() check (unlike get_sca_issues in this same file, which maps 404/non-2xx explicitly).

Impact: During companion rollout (or any 401/403/404/500 HTML/JSON error body), users get Failed to parse response then the generic check-token/connection path in list.rs, and the 404 string match for scan/project-does-not-exist never fires. A wrong path (/issues/quality vs /issues/code-quality) is easy to miss.

Fix: Mirror get_sca_issues: inspect status before parse; map 404/401/5xx to distinct errors that list.rs can surface. Prefer client.get(&endpoint).query(...) so project names are encoded and page_size cannot be appended as &page_size= without a ? when page is None.

Comment thread src/utils/api.rs
Comment on lines +1196 to +1227
fn deserializes_code_quality_issue_response() {
// Code quality issues carry a free-form classification label (no CWE) and
// must deserialize into the same Issue struct used for security issues.
let body = r#"{
"status": "ok",
"page": 1,
"total_pages": 1,
"total_issues": 1,
"issues": [
{
"id": "11111111-1111-1111-1111-111111111111",
"urgency": "ME",
"created_at": "2026-01-01T00:00:00Z",
"status": "open",
"classification": {
"id": "Maintainability",
"name": "Maintainability",
"description": null
},
"location": {
"file": {"name": "app.py", "language": "python", "path": "app/app.py"},
"project": {"name": "proj", "branch": "main", "git_sha": "abc"},
"line_number": 20
},
"auto_triage": {"false_positive_detection": {"status": "valid"}},
"auto_fix_suggestion": {"status": "no_fix"}
}
]
}"#;

let parsed: ProjectIssuesResponse =
serde_json::from_str(body).expect("should parse code quality response");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test passes without exercising the new behavior (endpoint contract / CLI wiring).

deserializes_code_quality_issue_response only round-trips a hand-written JSON fixture into ProjectIssuesResponse. It would still pass if:

  • scan path were /scan/{id}/issues/code-quality (or project path /issues/quality) — the PR uses asymmetric /scan/.../issues/quality vs /issues/code-quality with zero assertion
  • --issues / --sca-issues / --code-quality mutual exclusion regressed
  • --code-quality --scan-id wrongly called get_scan_issues

Impact: False confidence for a net-new production API surface.

Fix: Add a unit/integration test that builds the request URL (or uses a mock HTTP server) and asserts both endpoint variants + query params; add a clap/CLI test that --issues --code-quality exits 1 and that --code-quality selects get_quality_issues.

Comment thread src/list.rs
corgea list used the current directory basename as the project key, while
corgea scan stores projects under determine_project_name (which prefers
the Git remote repository name). This caused 'Project not found' when the
checkout directory differed from the repo name, including Git worktrees.

Use the same determine_project_name helper for list so the lookup key
matches what scans are stored under. Applies to --issues, --sca-issues,
--code-quality, and the default scan listing.

Co-authored-by: ibrahim <ibrahim@corgea.com>
- Add a --project-name flag to 'corgea list', matching 'corgea scan', and
  thread it through determine_project_name so users can override the
  resolved project key explicitly.
- Gate blocking-rules enrichment to security listings only. Previously
  'corgea list --code-quality --scan-id' ran check_blocking_rules and
  hard-exited on any failure (even after the CQ fetch succeeded), and
  could render Blocking columns driven by non-CQ findings.

Co-authored-by: ibrahim <ibrahim@corgea.com>
Comment thread src/list.rs Outdated

@yhoztak yhoztak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

cursoragent and others added 2 commits August 5, 2026 12:25
Resolves list/main conflicts on top of the COR-1577 project resolution
work: the code quality listing now goes through ProjectSelector and
resolve_project_or_exit like every other list mode.

Co-authored-by: Ibrahim Rahhal <ibrahim.rahhal3636@gmail.com>
Adds end-to-end coverage for the two documented code quality routes, the
project resolution they share with --issues, the blocking-rules gate, and
the 404 mapping, plus a unit test pinning the request paths and query.
Documents the flag in the agent skill.

Co-authored-by: Ibrahim Rahhal <ibrahim.rahhal3636@gmail.com>
@Ibrahimrahhal
Ibrahimrahhal merged commit c11add3 into main Aug 5, 2026
17 checks passed
@Ibrahimrahhal
Ibrahimrahhal deleted the cursor/expose-code-quality-findings-c31f branch August 5, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants